﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TelitUploadPython
{
    enum StatusMessage
    {
        TryOpenComPort,
        OpenComPortSuccess,
        CloseComPortSuccess,
        ErrorOpenComPort,
        BeginUploadFile,
        ErrorUpload,
        Ok,
        OpenFileError,
        FileNotFound,
        WaitModemReady,
        TimeoutUpload,
        UploadSuccess
    }

    enum ExpectArrayMode
    {
        Or,
        And,
        NoExpect
    }

    public partial class Form1 : Form
    {
        SerialPort serialPort = new SerialPort();
        private delegate void SetRichDeleg(string text);
        private delegate void SetTextDeleg(StatusMessage message, Color color, bool en);
        private delegate void SetListBoxDeleg(string text, bool adding, int pos);
        bool showRich = true;
        bool canRead = true;

        public Form1()
        {
            InitializeComponent();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Python compile files (*.pyo)|*.pyo|All files (*.*)|*.*";
            open.FilterIndex = 0;
            open.Multiselect = false;

            try
            {
                if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    textBox1.Text = open.FileName;
                    FileInfo f = new FileInfo(textBox1.Text.Trim());
                    label5.Text = String.Format("{0} байт", f.Length);
                    label6.ForeColor = Color.Green;
                    label6.Text = "OK";
                    button1.Enabled = true;
                }
            }
            catch
            {
                label5.Text = String.Format("{0} байт", 0);
                label6.ForeColor = Color.Red;
                label6.Text = "Ошибка открытия файла";
                button1.Enabled = false;
            }
        }

        private void ShowStatusMessage(StatusMessage message, Color color, int delay = 0)
        {
            Thread.Sleep(delay);

            if (message == TelitUploadPython.StatusMessage.TryOpenComPort)
            {
                label6.ForeColor = color;
                label6.Text = "Открываю порт модема...";
            }

            if (message == TelitUploadPython.StatusMessage.ErrorOpenComPort)
            {
                label6.ForeColor = color;
                label6.Text = "Ошибка открытия COM порта";
            }

            if (message == TelitUploadPython.StatusMessage.OpenComPortSuccess)
            {
                label6.ForeColor = color;
                label6.Text = "Порт модема открыт";
            }

            if (message == TelitUploadPython.StatusMessage.CloseComPortSuccess)
            {
                label6.ForeColor = color;
                label6.Text = "Порт модема закрыт";
            }

            if (message == TelitUploadPython.StatusMessage.WaitModemReady)
            {
                label6.ForeColor = color;
                label6.Text = "Ожидание готовности от модема...";
            }

            if (message == TelitUploadPython.StatusMessage.BeginUploadFile)
            {
                label6.ForeColor = color;
                label6.Text = "Идет загрузка питона...";
            }

            if (message == TelitUploadPython.StatusMessage.ErrorUpload)
            {
                label6.ForeColor = color;
                label6.Text = "Ошибка загрузки файла";
            }

            if (message == TelitUploadPython.StatusMessage.TimeoutUpload)
            {
                label6.ForeColor = color;
                label6.Text = "Таймаут ожидания данных модемом";
            }

            if (message == TelitUploadPython.StatusMessage.UploadSuccess)
            {
                label6.ForeColor = color;
                label6.Text = "Файл успешно загружен";
            }

            Application.DoEvents();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ShowStatusMessage(StatusMessage.TryOpenComPort, Color.Green, 200);

            try
            {
                comboBox1.Enabled = false;
                comboBox2.Enabled = false;
                button1.Enabled = false;
                button2.Enabled = false;
                groupBox5.Enabled = false;
                Application.DoEvents();

                FileInfo f = new FileInfo(textBox1.Text.Trim());
                label5.Text = String.Format("{0} байт", f.Length);

                string[] expect = new string[3];
                expect[0] = "OK";
                expect[1] = "ERROR";
                expect[2] = ">>>";

                string upload = String.Format("AT#WSCRIPT=\"{0}\",{1}\r\n", System.IO.Path.GetFileName(textBox1.Text.Trim()), f.Length);
                string res = WriteSerialPort(serialPort, upload, expect, ExpectArrayMode.Or, 10);
                
                serialPort.ReadExisting();
                ShowStatusMessage(StatusMessage.WaitModemReady, Color.Green, 1000);

                FileStream fs = File.OpenRead(textBox1.Text.Trim());
                int length = (int)fs.Length;
                byte[] b = new byte[1];
                int count = 0;

                progressBar1.Maximum = length;
                progressBar1.Value = 0;

                ShowStatusMessage(StatusMessage.BeginUploadFile, Color.Olive);
                res = "";
                showRich = false;

                for (int i = 0; i < length; i++)
                {
                    byte myByte = Convert.ToByte(fs.ReadByte());
                    b[0] = myByte;
                    
                    if (count == length-1)
                    {
                        res = WriteSerialPort(serialPort, b, expect, ExpectArrayMode.Or, 1000);
                    }
                    else
                    {
                        WriteSerialPort(serialPort, b, expect, ExpectArrayMode.NoExpect, 500);
                    }
                    progressBar1.Value = count + 1;
                    //label5.Text = String.Format("{0} из {1} байт загружено", count+1, length);
                    Application.DoEvents();
                    count++;
                }
                fs.Close();

                showRich = true;

                if (res.IndexOf(">>>") > 0)
                {
                    ShowStatusMessage(StatusMessage.UploadSuccess, Color.Green);
                    button6.PerformClick();
                }
                else
                {
                    ShowStatusMessage(StatusMessage.ErrorUpload, Color.Red);
                }

                comboBox1.Enabled = true;
                comboBox2.Enabled = true;
                button1.Enabled = true;
                button2.Enabled = true;
                groupBox5.Enabled = true;
            }
            catch 
            {
                showRich = true;
                comboBox1.Enabled = true;
                comboBox2.Enabled = true;
                groupBox5.Enabled = true;
                button1.Enabled = true;
                button2.Enabled = true;
                ShowStatusMessage(StatusMessage.ErrorUpload, Color.Red);
            }
        }

        private bool OpenPort()
        {
            try
            {
                if (serialPort.IsOpen) { return true; }

                serialPort.PortName = comboBox1.Text;
                if (radioButton1.Checked) { serialPort.BaudRate = 2400; }
                if (radioButton2.Checked) { serialPort.BaudRate = 9600; }
                if (radioButton4.Checked) { serialPort.BaudRate = 19200; }
                if (radioButton6.Checked) { serialPort.BaudRate = 38400; }
                if (radioButton5.Checked) { serialPort.BaudRate = 57600; }
                if (radioButton3.Checked) { serialPort.BaudRate = 115200; }
                if (comboBox2.SelectedIndex == 0) { serialPort.Handshake = Handshake.RequestToSend; }
                if (comboBox2.SelectedIndex == 1) { serialPort.Handshake = Handshake.None; }
                serialPort.NewLine = "\r\n";
                serialPort.DataBits = 8;
                serialPort.Parity = Parity.None;
                serialPort.StopBits = StopBits.One;

                serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                serialPort.Open();

                if (serialPort.IsOpen)
                {
                    panel1.BackColor = Color.Lime;
                    ShowStatusMessage(StatusMessage.OpenComPortSuccess, Color.Green, 200);
                }
                else
                {
                    panel1.BackColor = Color.Red;
                    ShowStatusMessage(StatusMessage.ErrorOpenComPort, Color.Red);
                }
                Application.DoEvents();

                return serialPort.IsOpen;
            }
            catch
            {
                panel1.BackColor = Color.Red;
                ShowStatusMessage(StatusMessage.ErrorOpenComPort, Color.Red);
                return false;
            }
        }

        private bool ClosePort()
        {
            try
            {
                if (!serialPort.IsOpen) { return true; }

                serialPort.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);

                while (serialPort.BytesToWrite > 0)
                {
                    Application.DoEvents();
                }
                serialPort.Close();

                if (serialPort.IsOpen)
                {
                    panel1.BackColor = Color.Lime;
                    ShowStatusMessage(StatusMessage.OpenComPortSuccess, Color.Green, 200);
                }
                else
                {
                    panel1.BackColor = Color.Red;
                    ShowStatusMessage(StatusMessage.CloseComPortSuccess, Color.Red);
                }
                Application.DoEvents();

                return true;
            }
            catch
            {
                panel1.BackColor = Color.Red;
                ShowStatusMessage(StatusMessage.ErrorOpenComPort, Color.Red);
                return false;
            }

        }

        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            this.Invoke(new EventHandler(DisplayText));
        }

        private void DisplayText(object sender, EventArgs e)
        {
            if (canRead) { richTextBox1.AppendText(serialPort.ReadExisting()); }
        }

        private void GetComPorts()
        {
            comboBox1.Enabled = false;
            string[] arrCom = SerialPort.GetPortNames();

            comboBox1.Items.Clear();

            if (arrCom.Length == 0) { return; };

            for (int i = 0; i < arrCom.Length; i++)
            {
                comboBox1.Items.Add(arrCom[i]);
            }

            comboBox1.SelectedIndex = 0;
            comboBox1.Enabled = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            GetComPorts();
            if (comboBox1.Items.Count != 0) { comboBox1.SelectedIndex = 0; }
            comboBox2.SelectedIndex = 0;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            GetComPorts();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort.IsOpen) ClosePort();
        }

        private void button6_Click(object sender, EventArgs e)
        {
            listBox1.BackColor = SystemColors.Control;

            listBox1.Items.Clear();

            string[] expect = new string[2];
            expect[0] = "OK";
            expect[1] = "ERROR";

            string RxString = WriteSerialPort(serialPort, "AT#LSCRIPT\r\n", expect, ExpectArrayMode.Or, 3000);

            if (RxString.IndexOf("OK") == -1) { return; }

            if (RxString.IndexOf("#LSCRIPT:") >= 0)
            {
                string[] arr = RxString.Split('\n');

                if (arr.Length == 0) { return; }

                for (int i = 0; i < arr.Length; i++)
                {
                    string tmp = arr[i];

                    int pos = tmp.IndexOf("#LSCRIPT: \"");

                    if (pos >= 0)
                    {
                        tmp = tmp.Remove(0, pos + 11);

                        pos = tmp.IndexOf("\"");
                        
                        if (pos == -1)
                        {
                            MessageBox.Show("Ошибка при обновлении\r\nПовторите еще раз!");
                            return;
                        }

                        string size = tmp.Substring(pos);
                        size = size.Remove(0, size.IndexOf(',')+1);
                        size = size.Remove(size.IndexOf('\r'));
                        tmp = tmp.Remove(pos);


                        listBox1.Items.Add(tmp + "  " + size + " байт");
                    }
                }
            }

            RxString = WriteSerialPort(serialPort, "AT#ESCRIPT?\r\n", expect, ExpectArrayMode.Or, 3000);

            if (RxString.IndexOf("OK") == -1) { return; }

            if (RxString.IndexOf("#ESCRIPT") >= 0 && listBox1.Items.Count != 0)
            {
                string[] arr = RxString.Split('\n');

                if (arr.Length == 0) { return; }

                for (int i = 0; i < arr.Length; i++)
                {
                    string tmp = arr[i];

                    int pos = tmp.IndexOf("#ESCRIPT: \"");

                    if (pos >= 0)
                    {
                        tmp = tmp.Remove(0, pos + 11);

                        pos = tmp.IndexOf("\"");

                        if (pos == -1)
                        {
                            MessageBox.Show("Ошибка при обновлении\r\nПовторите еще раз!");
                            return;
                        }

                        tmp = tmp.Remove(pos);

                        for (int a = 0; a < listBox1.Items.Count; a++)
                        {
                            if (tmp.Trim() != "")
                            {
                                if (listBox1.Items[a].ToString().IndexOf(tmp.Trim()) >= 0) { listBox1.Items[a] = "[*]" + listBox1.Items[a]; }
                            }
                        }
                    }
                }
            }

            listBox1.BackColor = SystemColors.Window;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex == -1) { return; }

            string tmp = listBox1.Items[listBox1.SelectedIndex].ToString().Trim();

            int pos = tmp.IndexOf("[Стартовый]");

            if (pos >= 0)
            {
                tmp = tmp.Remove(pos);
            }

            tmp = tmp.Substring(0, tmp.IndexOf(' '));

            string[] expect = new string[2];
            expect[0] = "OK";
            expect[1] = "ERROR";

            string d = WriteSerialPort(serialPort, "AT#DSCRIPT=\"" + tmp.Trim() + "\"\r\n", expect, ExpectArrayMode.Or, 3000);

            if (d.IndexOf("OK") == -1) { return; }

            button6.PerformClick();
        }

        private void button7_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex == -1) { return; }

            string tmp = listBox1.Items[listBox1.SelectedIndex].ToString().Trim();

            int pos = tmp.IndexOf("[Стартовый]");

            if (pos >= 0)
            {
                tmp = tmp.Remove(pos);
            }

            tmp = tmp.Substring(0, tmp.IndexOf(' '));

            string[] expect = new string[2];
            expect[0] = "OK";
            expect[1] = "ERROR";

            string d = WriteSerialPort(serialPort, "AT#ESCRIPT=\"" + tmp.Trim() + "\"\r\n", expect, ExpectArrayMode.Or, 3000);

            if (d.IndexOf("OK") == -1) { return; }

            button6.PerformClick();
        }

        private void button5_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex == -1) { return; }

            string tmp = listBox1.Items[listBox1.SelectedIndex].ToString().Trim();

            int pos = tmp.IndexOf("[Стартовый]");

            if (pos >= 0)
            {
                tmp = tmp.Remove(pos);
            }

            string[] expect = new string[2];
            expect[0] = "OK";
            expect[1] = "ERROR";

            string d = WriteSerialPort(serialPort, "AT#ESCRIPT=\"\"\r\n", expect, ExpectArrayMode.Or, 3000);

            if (d.IndexOf("OK") == -1) { return; }

            button6.PerformClick();
        }

        private void button8_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
        }

        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {
            richTextBox1.ScrollToCaret();
        }

        private string WriteSerialPort(SerialPort Serial, string TextToSend, string Expect, int Timeout)
        {
            return WriteSerialPort(Serial, System.Text.Encoding.UTF8.GetBytes(TextToSend), Expect, null, ExpectArrayMode.Or, Timeout, true);
        }

        private string WriteSerialPort(SerialPort Serial, string TextToSend, string Expect, ExpectArrayMode Mode, int Timeout)
        {
            return WriteSerialPort(Serial, System.Text.Encoding.UTF8.GetBytes(TextToSend), Expect, null, Mode, Timeout, true);
        }

        private string WriteSerialPort(SerialPort Serial, string TextToSend, string[] ExpectArray, ExpectArrayMode Mode, int Timeout)
        {
            return WriteSerialPort(Serial, System.Text.Encoding.UTF8.GetBytes(TextToSend), "", ExpectArray, Mode, Timeout, true);
        }

        private string WriteSerialPort(SerialPort Serial, byte[] BytesToSend, string[] ExpectArray, ExpectArrayMode Mode, int Timeout)
        {
            return WriteSerialPort(Serial, BytesToSend, "", ExpectArray, Mode, Timeout, true);
        }

        private string WriteSerialPort(SerialPort Serial, string TextToSend, string Expect, int Timeout, bool DiscardBuffer)
        {
            return WriteSerialPort(Serial, System.Text.Encoding.UTF8.GetBytes(TextToSend), Expect, null, ExpectArrayMode.Or, Timeout, DiscardBuffer);
        }

        private string WriteSerialPort(SerialPort Serial, string TextToSend, string[] ExpectArray, ExpectArrayMode Mode, int Timeout, bool DiscardBuffer)
        {
            return WriteSerialPort(Serial, System.Text.Encoding.UTF8.GetBytes(TextToSend), "", ExpectArray, Mode, Timeout, DiscardBuffer);
        }

        private string WriteSerialPort(SerialPort Serial, byte[] BytesToSend, string Expect, string[] ExpectArray, ExpectArrayMode Mode, int Timeout, bool DiscardBuffer)
        {
            try
            {
                if (BytesToSend.Length != 0) // Если есть текст на отправку в COM порт
                {
                    canRead = false;

                    if (showRich) { richTextBox1.AppendText("> " + Encoding.UTF8.GetString(BytesToSend) + "\r\n"); }

                    if (DiscardBuffer)
                    {
                        while (Serial.BytesToRead > 0)
                        {
                            Serial.ReadExisting();
                        }
                    }

                    lock (Serial)
                    {
                        Serial.Write(BytesToSend, 0, BytesToSend.Length); // ...загоняем ее в порт...
                        while (Serial.BytesToWrite > 0) // ...и проталкиваем, чтобы не застрало :)
                        {
                            Application.DoEvents();
                        }

                        if (Mode == ExpectArrayMode.NoExpect) { canRead = true; return ""; }

                        string res = ""; // Здесь будем хранить результаты из СОМ порта
                        bool resComplete = false; // Флаг успешного запроса
                        int Coincidence = 0; // Счетчик совпадений

                        for (int i = 0; i <= Timeout / 100; i++) // Цикл на указаный таймаут
                        {
                            if (Serial.BytesToRead > 0) // Если есть данные в СОМ порту
                            {
                                byte[] inBuffer = new byte[Serial.BytesToRead]; // Читаем их
                                Serial.Read(inBuffer, 0, Serial.BytesToRead);
                                res += new String(System.Text.Encoding.UTF8.GetChars(inBuffer)); // Помещаем их в строку res

                                if (ExpectArray != null) // Если выбран режим массива
                                {

                                    if (Mode == ExpectArrayMode.And && !resComplete) // Если режим И
                                    {
                                        for (int a = 0; a < ExpectArray.Length; a++)
                                        {
                                            if (res.IndexOf(ExpectArray[a]) >= 0)
                                            {
                                                Coincidence++; // При совпадении увеличиваем счетчик
                                            }
                                        }

                                        if (Coincidence == ExpectArray.Length) { resComplete = true; } // Когда счетчик совпадений равен длине массива, то все ОК
                                    }

                                    if (Mode == ExpectArrayMode.Or && !resComplete) // Если режим ИЛИ
                                    {
                                        for (int a = 0; a < ExpectArray.Length; a++)
                                        {
                                            if (res.IndexOf(ExpectArray[a]) >= 0) // Первое же совпадение и...
                                            {
                                                resComplete = true; // ...говорим ОК
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    if (Expect != "") // Если просто строка...
                                    {
                                        if (res.IndexOf(Expect) >= 0) // ...и она совпалась...
                                        {
                                            resComplete = true; // ... то говорим ОК
                                        }
                                    }
                                }
                            }

                            if (Serial.BytesToRead == 0 && resComplete == true) // Еще раз убеждаемся, что в СОМ порту ничего не осталось и мы выполнили все совпадения
                            {
                                while (Serial.BytesToRead > 0)
                                {
                                    Serial.ReadExisting();
                                }
                                if (showRich) { richTextBox1.AppendText(res + "\r\n"); }
                                canRead = true;
                                return res; // Вываливаемся из процедуры с нашими данными
                            }

                            Thread.Sleep(100);
                        }

                        while (Serial.BytesToRead > 0)
                        {
                            Serial.ReadExisting();
                        }

                        canRead = true;
                        return res;
                    }
                }
                else
                {
                    canRead = true;
                    return "";
                }
            }
            catch
            {
                while (Serial.BytesToRead > 0)
                {
                    Serial.ReadExisting();
                }
                canRead = true;
                return "";
            }
        }

        private string QuickWrite(string text)
        {
            string[] expect = new string[2];
            expect[0] = "OK";
            expect[1] = "ERROR";
            textBox2.Focus();
            return WriteSerialPort(serialPort, text + "\r\n", expect, ExpectArrayMode.Or, 3000);
        }

        private void button11_Click(object sender, EventArgs e)
        {
            QuickWrite((sender as Button).Text);
        }

        private void button21_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(textBox2.Text)) { return; }

            string eol = "";

            if (checkBox1.Checked) { eol = "\r\n"; }

            WriteSerialPort(serialPort, textBox2.Text.Trim() + eol, "\r\n", 100);
        }

        private void textBox2_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter) { button21.PerformClick(); }
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            textBox2.Focus();
        }

        private void button22_Click(object sender, EventArgs e)
        {
            button22.Enabled = false;
            groupBox1.Enabled = false;
            groupBox3.Enabled = false;
            groupBox4.Enabled = false;
            groupBox5.Enabled = true;
            groupBox7.Enabled = true;

            button23.Enabled = true;
            OpenPort();
            textBox2.Focus();
        }

        private void button23_Click(object sender, EventArgs e)
        {
            button23.Enabled = false;
            button22.Enabled = true;
            groupBox1.Enabled = true;
            groupBox3.Enabled = true;
            groupBox4.Enabled = true;
            groupBox5.Enabled = false;
            groupBox7.Enabled = false;
            ClosePort();
        }

        private void button19_Click(object sender, EventArgs e)
        {
            QuickWrite("ATE0");
        }

        private void button33_Click(object sender, EventArgs e)
        {
            InputBox input = new InputBox();
            input.label1.Text = "Введите USSD запрос";
            input.Text = "USSD (AT+CUSD=1,\"......\")";
            if (input.ShowDialog()== System.Windows.Forms.DialogResult.OK)
            {
                QuickWrite("AT+CUSD=1,\"" + input.textBox1.Text.Trim() + "\"");
            }


        }

    }
}
